﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data;
using System.Collections.ObjectModel;
using ProgNetComponents.Validation.Rules;
using ProgNetComponents.Validation;
using System.Collections;
using ProgNetComponents.Utils.EventHandlers;
using ProgNetComponents.Classes;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProgNetComponentsDx.Validation
{
    [TypeConverter(typeof(ProgNetComponentsDx.Validation.Design.ValidationRuleForTableTypeConverter))]
    public class ValidationRuleForTable : IDisposable
    {
        public event DataRowEventHandler ValidationPassed;
        public event DataRowEventHandler ValidationFailed;

        protected virtual void OnValidationPassed(ValidationRule rule, DataRow row)
        {
            if (ValidationPassed != null)
                ValidationPassed(rule, new DataRowEventArgs(row));
        }
        protected virtual void OnValidationFailed(ValidationRule rule, DataRow row)
        {
            if (ValidationFailed != null)
                ValidationFailed(rule, new DataRowEventArgs(row));
        }

        private DataTable _Table;

        public ValidationRuleForTable()
            : this(null, null, null)
        {

        }
       
        private void Rules_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                if (e.NewItems != null)
                {
                    foreach (var item in e.NewItems)
                    {
                        if (item is IDataValidationRule)
                        {
                            ((IDataValidationRule)item).DataSource = this.Table;
                        }
                    }
                }
            }
            else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove ||
                e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            {
                if (e.OldItems != null)
                {
                    foreach (var item in e.OldItems)
                    {
                        if (item is IDataValidationRule)
                        {
                            ((IDataValidationRule)item).DataSource = null;
                        }
                    }
                }
            }
        }

        public ValidationRuleForTable(DataTable table):this(table, null, null)
        {
        }
        public ValidationRuleForTable(DataTable table, ValidationRule[] rules):this(table, null, rules)
        {
        }
        public ValidationRuleForTable(DataTable table, string filter, ValidationRule[] rules)
        {
            Rules = new ValidationRuleCollection();
            Rules.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Rules_CollectionChanged);

            Table = table;
            Filter = filter;
            if (rules != null)
                Rules.AddRange(rules);
        }

        public DataTable Table
        {
            get
            {
                return _Table;
            }
            set
            {
                _Table = value;
                foreach (var rule in Rules)
                {
                    if (rule is IDataValidationRule)
                    {
                        ((IDataValidationRule)rule).DataSource = this.Table;
                    }
                }
            }
        }
       
        public string Filter { get; set; }

        public ValidationRuleCollection Rules { get; private set; }
        public override string ToString()
        {
            if (Table != null)
                return Table.TableName;
            return base.ToString();
        }

        public bool ValidateAll(bool stopOnFirstError, bool clearValidationSettings = true)
        {
            bool res = true;
            if (Table != null)
            {
                if (clearValidationSettings)
                    Rules.ClearValidationSettings();

                DataRow[] rows = Table.Select(Filter);
                foreach (DataRow row in rows)
                {
                    foreach (var r in Rules)
                    {
                        if (!ValidateRule(row, r, stopOnFirstError))
                        {
                            res = false;
                            if (stopOnFirstError)
                                return res;
                        }
                    }
                }
            }
            return res;
        }

        private bool ValidateRule(DataRow row, ValidationRule r, bool stopOnFirstError)
        {
            bool result = true;
            r.ValidationFailed += new DataRowEventHandler(r_ValidationFailed);
            r.ValidationPassed += new DataRowEventHandler(r_ValidationPassed);
            try
            {
                result = r.ValidateRow(row);
            }
            finally
            {
                r.ValidationFailed -= new DataRowEventHandler(r_ValidationFailed);
                r.ValidationPassed -= new DataRowEventHandler(r_ValidationPassed);
            }
            if(result)
                OnValidationPassed(r, row);
            else
                OnValidationFailed(r, row);
            return result;
        }
        private void r_ValidationPassed(object sender, DataRowEventArgs e)
        {
            OnValidationPassed(sender as ValidationRule, e.Row);
        }
        private void r_ValidationFailed(object sender, DataRowEventArgs e)
        {
            OnValidationFailed(sender as ValidationRule, e.Row);
        }

        public void Dispose()
        {
            foreach (var r in this.Rules)
            {
                r.Dispose();
            }
        }
    }

    public class ValidationRulesForTableCollection : Collection<ValidationRuleForTable>, IDisposable
    {
        public event DataRowEventHandler ValidationPassed;
        public event DataRowEventHandler ValidationFailed;

        protected virtual void OnValidationPassed(ValidationRule rule, DataRow row)
        {
            if (ValidationPassed != null)
                ValidationPassed(rule, new DataRowEventArgs(row));
        }
        protected virtual void OnValidationFailed(ValidationRule rule, DataRow row)
        {
            if (ValidationFailed != null)
                ValidationFailed(rule, new DataRowEventArgs(row));
        }

        private void r_ValidationPassed(object sender, DataRowEventArgs e)
        {
            OnValidationPassed(sender as ValidationRule, e.Row);
        }
        private void r_ValidationFailed(object sender, DataRowEventArgs e)
        {
            OnValidationFailed(sender as ValidationRule, e.Row);
        }

        public void Dispose()
        {
            foreach (var r in this)
                r.Dispose();
        }

        public bool ValidateAll(bool stopOnFirstError, bool clearValidationSettings = true)
        {
            bool result = true;

            foreach (var r in this)
            {
                if (!ValidateRule(r, stopOnFirstError, clearValidationSettings))
                {
                    result = false;
                    if (stopOnFirstError)
                        return result;
                }
            }
            return result;
        }

        private bool ValidateRule(ValidationRuleForTable r, bool stopOnFirstError, bool clearValidationSettings)
        {
            bool result = true;
            r.ValidationFailed += new DataRowEventHandler(r_ValidationFailed);
            r.ValidationPassed += new DataRowEventHandler(r_ValidationPassed);
            try { result = r.ValidateAll(stopOnFirstError, clearValidationSettings); }
            finally
            {
                r.ValidationFailed -= new DataRowEventHandler(r_ValidationFailed);
                r.ValidationPassed -= new DataRowEventHandler(r_ValidationPassed);
            }
            return result;
        }
    }
}
